home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectSound / CaptureSound / capturesound.cpp next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  33.0 KB  |  1,002 lines

  1. //----------------------------------------------------------------------------
  2. // File: CaptureSound.cpp
  3. //
  4. // Desc: The CaptureSound sample shows how to use DirectSoundCapture to capture 
  5. //       sound into a wave file 
  6. //
  7. // Copyright (c) Microsoft Corp. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #include "dxstdafx.h"
  10. #include <commdlg.h>
  11. #include "resource.h"
  12.  
  13.  
  14.  
  15.  
  16. //-----------------------------------------------------------------------------
  17. // Function-prototypes
  18. //-----------------------------------------------------------------------------
  19. HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid );
  20. HRESULT FreeDirectSound();
  21.  
  22. INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName,
  23.                                      VOID* pContext );
  24. INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  25. INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  26. HRESULT OnInitDevicesDialog( HWND hDlg );
  27. HRESULT OnInitFormatsDialog( HWND hDlg );
  28. HRESULT ScanAvailableInputFormats();
  29. VOID    GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx );
  30. HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported );
  31. VOID    ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName );
  32. HRESULT OnInputFormatBoxSelected( HWND hDlg );
  33. HRESULT OnFormatsOK( HWND hDlg );
  34.  
  35. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  36. HRESULT OnInitMainDialog( HWND hDlg );
  37. VOID    OnSaveSoundFile( HWND hDlg );
  38. HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput );
  39. HRESULT InitNotifications();
  40. HRESULT StartOrStopRecord( BOOL bStartRecording );
  41. HRESULT RecordCapturedData();
  42.  
  43.  
  44.  
  45.  
  46. //-----------------------------------------------------------------------------
  47. // Defines, constants, and global variables
  48. //-----------------------------------------------------------------------------
  49. #define NUM_REC_NOTIFICATIONS  16
  50. #define MAX(a,b)        ( (a) > (b) ? (a) : (b) )
  51.  
  52. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  53. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  54.  
  55. LPDIRECTSOUNDCAPTURE       g_pDSCapture         = NULL;
  56. LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture        = NULL;
  57. LPDIRECTSOUNDNOTIFY        g_pDSNotify          = NULL;
  58. HINSTANCE                  g_hInst              = NULL;
  59. GUID                       g_guidCaptureDevice  = GUID_NULL;
  60. BOOL                       g_bRecording;
  61. WAVEFORMATEX               g_wfxInput;
  62. DSBPOSITIONNOTIFY          g_aPosNotify[ NUM_REC_NOTIFICATIONS + 1 ];  
  63. HANDLE                     g_hNotificationEvent; 
  64. BOOL                       g_abInputFormatSupported[20];
  65. DWORD                      g_dwCaptureBufferSize;
  66. DWORD                      g_dwNextCaptureOffset;
  67. DWORD                      g_dwNotifySize;
  68. CWaveFile*                  g_pWaveFile;
  69.  
  70.  
  71.  
  72.  
  73. //-----------------------------------------------------------------------------
  74. // Name: WinMain()
  75. // Desc: Entry point for the application.  Since we use a simple dialog for 
  76. //       user interaction we don't need to pump messages.
  77. //-----------------------------------------------------------------------------
  78. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, 
  79.                       INT nCmdShow )
  80. {
  81.     HRESULT hr;
  82.     DWORD dwResult;
  83.     MSG   msg;
  84.     BOOL  bDone;
  85.     HWND  hDlg;
  86.  
  87.     g_hInst = hInst;
  88.     g_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  89.  
  90.     // Create the main dialog box, but keep it hidden for now
  91.     hDlg = CreateDialog( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc );
  92.  
  93.  
  94.     // Display the formats dialog box, and show it
  95.     dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_DEVICES), 
  96.                                  NULL, DevicesDlgProc );
  97.     if( dwResult != IDOK )
  98.     {
  99.         // The user canceled, so stop message pump, 
  100.         // and fall through to the cleanup code
  101.         PostQuitMessage( 0 );
  102.     }
  103.     else
  104.     {
  105.         // Init DirectSound
  106.         if( FAILED( hr = InitDirectSound( hDlg, &g_guidCaptureDevice ) ) )
  107.         {
  108.             DXTRACE_ERR_MSGBOX( TEXT("InitDirectSound"), hr );
  109.             MessageBox( hDlg, L"Error initializing DirectSound.  Sample will now exit.", 
  110.                         L"DirectSound Sample", MB_OK | MB_ICONERROR );
  111.             PostQuitMessage( 0 );
  112.             dwResult = IDCANCEL;
  113.         }
  114.     }
  115.  
  116.  
  117.     // Display the formats dialog box, and show it
  118.     if( dwResult == IDOK )
  119.     {
  120.         dwResult = (DWORD)DialogBox( hInst, MAKEINTRESOURCE(IDD_FORMATS), 
  121.                                      NULL, FormatsDlgProc );
  122.     }
  123.  
  124.     if( dwResult != IDOK )
  125.     {
  126.         // The user canceled, so stop message pump, 
  127.         // and fall through to the cleanup code
  128.         PostQuitMessage( 0 );
  129.     }
  130.     else
  131.     {
  132.         WAVEFORMATEX wfxInput;
  133.         TCHAR        strInputFormat[255];
  134.         HWND         hInputFormatText;
  135.  
  136.         hInputFormatText = GetDlgItem( hDlg, IDC_MAIN_INPUTFORMAT_TEXT );
  137.  
  138.         ZeroMemory( &wfxInput, sizeof(wfxInput));
  139.         g_pDSBCapture->GetFormat( &wfxInput, sizeof(wfxInput), NULL );
  140.         ConvertWaveFormatToString( &wfxInput, strInputFormat );   
  141.  
  142.         SetWindowText( hInputFormatText, strInputFormat );
  143.  
  144.         g_bRecording = FALSE;
  145.         ShowWindow( hDlg, SW_SHOW ); 
  146.     }
  147.  
  148.     bDone = FALSE;
  149.     while( !bDone ) 
  150.     { 
  151.         dwResult = MsgWaitForMultipleObjects( 1, &g_hNotificationEvent, 
  152.                                               FALSE, INFINITE, QS_ALLEVENTS );
  153.         switch( dwResult )
  154.         {
  155.             case WAIT_OBJECT_0 + 0:
  156.                 // g_hNotificationEvents[0] is signaled
  157.  
  158.                 // This means that DirectSound just finished playing 
  159.                 // a piece of the buffer, so we need to fill the circular 
  160.                 // buffer with new sound from the wav file
  161.  
  162.                 if( FAILED( hr = RecordCapturedData() ) )
  163.                 {
  164.                     DXTRACE_ERR_MSGBOX( TEXT("RecordCapturedData"), hr );
  165.                     MessageBox( hDlg, L"Error handling DirectSound notifications. "
  166.                                L"Sample will now exit.", L"DirectSound Sample", 
  167.                                MB_OK | MB_ICONERROR );
  168.                     bDone = TRUE;
  169.                 }
  170.                 break;
  171.  
  172.             case WAIT_OBJECT_0 + 1:
  173.                 // Windows messages are available
  174.                 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  175.                 { 
  176.                     if( !IsDialogMessage( hDlg, &msg ) )  
  177.                     {
  178.                         TranslateMessage( &msg ); 
  179.                         DispatchMessage( &msg ); 
  180.                     }
  181.  
  182.                     if( msg.message == WM_QUIT )
  183.                         bDone = TRUE;
  184.                 }
  185.                 break;
  186.         }
  187.     }
  188.  
  189.     // Stop the capture and read any data that was not caught by a notification
  190.     StartOrStopRecord( FALSE );
  191.  
  192.     // Clean up everything
  193.     FreeDirectSound();
  194.  
  195.     CloseHandle( g_hNotificationEvent );
  196.  
  197.     return TRUE;
  198. }
  199.  
  200.  
  201.  
  202.  
  203. //-----------------------------------------------------------------------------
  204. // Name: InitDirectSound()
  205. // Desc: Initilizes DirectSound
  206. //-----------------------------------------------------------------------------
  207. HRESULT InitDirectSound( HWND hDlg, GUID* pDeviceGuid )
  208. {
  209.     HRESULT hr;
  210.  
  211.     ZeroMemory( &g_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * 
  212.                                (NUM_REC_NOTIFICATIONS + 1) );
  213.     g_dwCaptureBufferSize = 0;
  214.     g_dwNotifySize        = 0;
  215.     g_pWaveFile           = NULL;
  216.  
  217.     // Initialize COM
  218.     if( FAILED( hr = CoInitialize(NULL) ) )
  219.         return DXTRACE_ERR_MSGBOX( TEXT("CoInitialize"), hr );
  220.  
  221.  
  222.     // Create IDirectSoundCapture using the preferred capture device
  223.     if( FAILED( hr = DirectSoundCaptureCreate( pDeviceGuid, &g_pDSCapture, NULL ) ) )
  224.         return DXTRACE_ERR_MSGBOX( TEXT("DirectSoundCaptureCreate"), hr );
  225.  
  226.     return S_OK;
  227. }
  228.  
  229.  
  230.  
  231.  
  232. //-----------------------------------------------------------------------------
  233. // Name: FreeDirectSound()
  234. // Desc: Releases DirectSound 
  235. //-----------------------------------------------------------------------------
  236. HRESULT FreeDirectSound()
  237. {
  238.     SAFE_DELETE( g_pWaveFile );
  239.  
  240.     // Release DirectSound interfaces
  241.     SAFE_RELEASE( g_pDSNotify );
  242.     SAFE_RELEASE( g_pDSBCapture );
  243.     SAFE_RELEASE( g_pDSCapture ); 
  244.  
  245.     // Release COM
  246.     CoUninitialize();
  247.  
  248.     return S_OK;
  249. }
  250.  
  251.  
  252.  
  253.  
  254. //-----------------------------------------------------------------------------
  255. // Name: DevicesDlgProc()
  256. // Desc: Handles dialog messages for devices dlg box
  257. //-----------------------------------------------------------------------------
  258. INT_PTR CALLBACK DevicesDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  259. {
  260.     HRESULT hr;
  261.  
  262.     switch( msg ) 
  263.     {
  264.     case WM_INITDIALOG:
  265.         if( FAILED( hr = OnInitDevicesDialog( hDlg ) ) )
  266.         {
  267.             DXTRACE_ERR_MSGBOX( TEXT("OnInitDevicesDialog"), hr );
  268.             MessageBox( hDlg, L"Error scanning DirectSoundCapture devices. "
  269.                         L"Sample will now exit.", L"DirectSound Sample", 
  270.                         MB_OK | MB_ICONERROR );
  271.             EndDialog( hDlg, IDABORT );
  272.         }
  273.         break;
  274.  
  275.     case WM_COMMAND:
  276.         switch( LOWORD(wParam) )
  277.         {
  278.             case IDCANCEL:
  279.                 EndDialog( hDlg, IDCANCEL );
  280.                 break;
  281.  
  282.             case IDOK:
  283.             {
  284.                 HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO );
  285.  
  286.                 // Get the index of the currently selected devices
  287.                 INT nCaptureIndex = (INT)SendMessage( hCaptureDeviceCombo, CB_GETCURSEL, 0, 0 ); 
  288.  
  289.                 // Get the GUID attached to the combo box item
  290.                 GUID* pCaptureGUID = (GUID*) SendMessage( hCaptureDeviceCombo, CB_GETITEMDATA, 
  291.                                                           nCaptureIndex, 0 );
  292.  
  293.                 // Remember that guid
  294.                 if( pCaptureGUID ) 
  295.                     g_guidCaptureDevice = *pCaptureGUID;
  296.  
  297.                 EndDialog( hDlg, IDOK );
  298.                 break;
  299.             }
  300.  
  301.             default:
  302.                 return FALSE; // Didn't handle message
  303.         }   
  304.         break;
  305.  
  306.     default:
  307.         return FALSE; // Didn't handle message
  308.     }
  309.  
  310.     return TRUE; // Handled message
  311. }
  312.  
  313.  
  314.  
  315.  
  316. //-----------------------------------------------------------------------------
  317. // Name: OnInitDevicesDialog()
  318. // Desc: Initializes the devices dialog
  319. //-----------------------------------------------------------------------------
  320. HRESULT OnInitDevicesDialog( HWND hDlg )
  321. {
  322.     // Enumerate the capture devices and place them in the combo box
  323.     HWND hCaptureDeviceCombo = GetDlgItem( hDlg, IDC_CAPTURE_DEVICE_COMBO );
  324.     DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)DSoundEnumCallback,
  325.                                  (VOID*)hCaptureDeviceCombo );
  326.  
  327.     // Select the first device in the combo box
  328.     SendMessage( hCaptureDeviceCombo, CB_SETCURSEL, 0, 0 );
  329.  
  330.     return S_OK;
  331. }
  332.  
  333.  
  334.  
  335.  
  336. //-----------------------------------------------------------------------------
  337. // Name: FormatsDlgProc()
  338. // Desc: Handles dialog messages for formats dlg box
  339. //-----------------------------------------------------------------------------
  340. INT_PTR CALLBACK FormatsDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  341. {
  342.     HRESULT hr;
  343.  
  344.     switch( msg ) 
  345.     {
  346.     case WM_INITDIALOG:
  347.         if( FAILED( hr = OnInitFormatsDialog( hDlg ) ) )
  348.         {
  349.             DXTRACE_ERR_MSGBOX( TEXT("OnInitFormatsDialog"), hr );
  350.             MessageBox( hDlg, L"Error scanning DirectSound formats. "
  351.                         L"Sample will now exit.", L"DirectSound Sample", 
  352.                         MB_OK | MB_ICONERROR );
  353.             EndDialog( hDlg, IDABORT );
  354.         }
  355.         break;
  356.  
  357.     case WM_COMMAND:
  358.         switch( LOWORD(wParam) )
  359.         {
  360.             case IDCANCEL:
  361.                 EndDialog( hDlg, IDCANCEL );
  362.                 break;
  363.  
  364.             case IDOK:
  365.                 if( FAILED( hr = OnFormatsOK( hDlg ) ) )
  366.                 {
  367.                     DXTRACE_ERR_MSGBOX( TEXT("OnFormatsOK"), hr );
  368.                     MessageBox( hDlg, L"Error accepting DirectSound formats. "
  369.                                 L"Sample will now exit.", L"DirectSound Sample", 
  370.                                 MB_OK | MB_ICONERROR );
  371.                     EndDialog( hDlg, IDABORT );
  372.                 }
  373.  
  374.                 break;
  375.  
  376.             case IDC_FORMATS_INPUT_LISTBOX:
  377.                 OnInputFormatBoxSelected( hDlg );
  378.                 break;
  379.             
  380.             default:
  381.                 return FALSE; // Didn't handle message
  382.         }   
  383.         break;
  384.  
  385.     default:
  386.         return FALSE; // Didn't handle message
  387.     }
  388.  
  389.     return TRUE; // Handled message
  390. }
  391.  
  392.  
  393.  
  394.  
  395. //-----------------------------------------------------------------------------
  396. // Name: OnInitFormatsDialog()
  397. // Desc: Initializes the formats dialog
  398. //-----------------------------------------------------------------------------
  399. HRESULT OnInitFormatsDialog( HWND hDlg )
  400. {
  401.     HRESULT hr;
  402.  
  403.     if( FAILED( hr = ScanAvailableInputFormats() ) )
  404.         return DXTRACE_ERR_MSGBOX( TEXT("ScanAvailableInputFormats"), hr );
  405.  
  406.     HWND hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  407.     FillFormatListBox( hInputList, g_abInputFormatSupported );
  408.  
  409.     return S_OK;
  410. }
  411.  
  412.  
  413.  
  414.  
  415. //-----------------------------------------------------------------------------
  416. // Name: ScanAvailableInputFormats()
  417. // Desc: Tests to see if 20 different standard wave formats are supported by
  418. //       the capture device 
  419. //-----------------------------------------------------------------------------
  420. HRESULT ScanAvailableInputFormats()
  421. {
  422.     HRESULT       hr;
  423.     WAVEFORMATEX  wfx;
  424.     HCURSOR       hCursor;
  425.     DSCBUFFERDESC dscbd;
  426.     LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer = NULL;
  427.     
  428.     // This might take a second or two, so throw up the hourglass
  429.     hCursor = GetCursor();
  430.     SetCursor( LoadCursor( NULL, IDC_WAIT ) );
  431.     
  432.     ZeroMemory( &wfx, sizeof(wfx));
  433.     wfx.wFormatTag = WAVE_FORMAT_PCM;
  434.  
  435.     ZeroMemory( &dscbd, sizeof(dscbd) );
  436.     dscbd.dwSize = sizeof(dscbd);
  437.  
  438.     // Try 20 different standard formats to see if they are supported
  439.     for( INT iIndex = 0; iIndex < 20; iIndex++ )
  440.     {
  441.         GetWaveFormatFromIndex( iIndex, &wfx );
  442.  
  443.         // To test if a capture format is supported, try to create a 
  444.         // new capture buffer using a specific format.  If it works
  445.         // then the format is supported, otherwise not.
  446.         dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
  447.         dscbd.lpwfxFormat = &wfx;
  448.         
  449.         if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, 
  450.                                                             &pDSCaptureBuffer, 
  451.                                                             NULL ) ) )
  452.             g_abInputFormatSupported[ iIndex ] = FALSE;
  453.         else
  454.             g_abInputFormatSupported[ iIndex ] = TRUE;
  455.  
  456.         SAFE_RELEASE( pDSCaptureBuffer );
  457.     }
  458.  
  459.     SetCursor( hCursor );
  460.  
  461.     return S_OK;
  462. }
  463.  
  464.  
  465.  
  466.  
  467. //-----------------------------------------------------------------------------
  468. // Name: GetWaveFormatFromIndex()
  469. // Desc: Returns 20 different wave formats based on nIndex
  470. //-----------------------------------------------------------------------------
  471. VOID GetWaveFormatFromIndex( INT nIndex, WAVEFORMATEX* pwfx )
  472. {
  473.     INT iSampleRate = nIndex / 4;
  474.     INT iType = nIndex % 4;
  475.  
  476.     switch( iSampleRate )
  477.     {
  478.         case 0: pwfx->nSamplesPerSec = 48000; break;
  479.         case 1: pwfx->nSamplesPerSec = 44100; break;
  480.         case 2: pwfx->nSamplesPerSec = 22050; break;
  481.         case 3: pwfx->nSamplesPerSec = 11025; break;
  482.         case 4: pwfx->nSamplesPerSec =  8000; break;
  483.     }
  484.  
  485.     switch( iType )
  486.     {
  487.         case 0: pwfx->wBitsPerSample =  8; pwfx->nChannels = 1; break;
  488.         case 1: pwfx->wBitsPerSample = 16; pwfx->nChannels = 1; break;
  489.         case 2: pwfx->wBitsPerSample =  8; pwfx->nChannels = 2; break;
  490.         case 3: pwfx->wBitsPerSample = 16; pwfx->nChannels = 2; break;
  491.     }
  492.  
  493.     pwfx->nBlockAlign = pwfx->nChannels * ( pwfx->wBitsPerSample / 8 );
  494.     pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
  495. }
  496.  
  497.  
  498.  
  499.  
  500. //-----------------------------------------------------------------------------
  501. // Name: FillFormatListBox()
  502. // Desc: Fills the format list box based on the availible formats
  503. //-----------------------------------------------------------------------------
  504. HRESULT FillFormatListBox( HWND hListBox, BOOL* aFormatSupported )
  505. {
  506.     TCHAR        strFormatName[255];
  507.     WAVEFORMATEX wfx;
  508.     DWORD        dwStringIndex;
  509.  
  510.     SendMessage( hListBox, LB_RESETCONTENT, 0, 0 );
  511.  
  512.     for( INT iIndex = 0; iIndex < 20; iIndex++ )
  513.     {
  514.         if( aFormatSupported[ iIndex ] )
  515.         {
  516.             // Turn the index into a WAVEFORMATEX then turn that into a
  517.             // string and put the string in the listbox
  518.             GetWaveFormatFromIndex( iIndex, &wfx );
  519.             ConvertWaveFormatToString( &wfx, strFormatName );
  520.             dwStringIndex = (DWORD)SendMessage( hListBox, LB_ADDSTRING, 0, 
  521.                                                 (LPARAM) (LPCTSTR) strFormatName );
  522.             SendMessage( hListBox, LB_SETITEMDATA, dwStringIndex, iIndex );
  523.         }
  524.     }
  525.  
  526.     return S_OK;
  527. }
  528.  
  529.  
  530.  
  531.  
  532. //-----------------------------------------------------------------------------
  533. // Name: ConvertWaveFormatToString()
  534. // Desc: Converts a wave format to a text string
  535. //-----------------------------------------------------------------------------
  536. VOID ConvertWaveFormatToString( WAVEFORMATEX* pwfx, TCHAR* strFormatName )
  537. {
  538.     wsprintf( strFormatName, 
  539.               TEXT("%u Hz, %u-bit %s"), 
  540.               pwfx->nSamplesPerSec, 
  541.               pwfx->wBitsPerSample, 
  542.               ( pwfx->nChannels == 1 ) ? TEXT("Mono") : TEXT("Stereo") );
  543. }
  544.  
  545.  
  546.  
  547.  
  548. //-----------------------------------------------------------------------------
  549. // Name: OnInputFormatBoxSelected()
  550. // Desc: Enables the OK button when there is a selection
  551. //-----------------------------------------------------------------------------
  552. HRESULT OnInputFormatBoxSelected( HWND hDlg )
  553. {
  554.     HWND hInputList  = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  555.     HWND hOK         = GetDlgItem( hDlg, IDOK );
  556.  
  557.     if( SendMessage( hInputList,  LB_GETCURSEL, 0, 0 ) != -1 )        
  558.         EnableWindow( hOK, TRUE );
  559.     else
  560.         EnableWindow( hOK, FALSE );
  561.  
  562.     return S_OK;
  563. }
  564.  
  565.  
  566.  
  567.  
  568. //-----------------------------------------------------------------------------
  569. // Name: OnFormatsOK()
  570. // Desc: Creates a capture buffer format based on what was selected
  571. //-----------------------------------------------------------------------------
  572. HRESULT OnFormatsOK( HWND hDlg )
  573. {
  574.     HRESULT       hr;
  575.     DWORD         dwInputSelect;
  576.     DWORD         dwInputWavIndex;
  577.     HWND          hInputList;
  578.  
  579.     hInputList = GetDlgItem( hDlg, IDC_FORMATS_INPUT_LISTBOX );
  580.  
  581.     dwInputSelect   = (DWORD)SendMessage( hInputList, LB_GETCURSEL, 0, 0 );
  582.     dwInputWavIndex = (DWORD)SendMessage( hInputList, LB_GETITEMDATA, dwInputSelect, 0 );
  583.  
  584.     ZeroMemory( &g_wfxInput, sizeof(g_wfxInput));
  585.     g_wfxInput.wFormatTag = WAVE_FORMAT_PCM;
  586.  
  587.     GetWaveFormatFromIndex( dwInputWavIndex, &g_wfxInput );
  588.  
  589.     if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) )
  590.         return DXTRACE_ERR_MSGBOX( TEXT("CreateCaptureBuffer"), hr );
  591.  
  592.     EndDialog( hDlg, IDOK );
  593.  
  594.     return S_OK;
  595. }
  596.  
  597.  
  598.  
  599.  
  600. //-----------------------------------------------------------------------------
  601. // Name: MainDlgProc()
  602. // Desc: Handles dialog messages
  603. //-----------------------------------------------------------------------------
  604. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  605. {
  606.     HRESULT hr;
  607.  
  608.     switch( msg ) 
  609.     {
  610.     case WM_INITDIALOG:
  611.         OnInitMainDialog( hDlg );
  612.         break;
  613.  
  614.     case WM_COMMAND:
  615.         switch( LOWORD(wParam) )
  616.         {
  617.             case IDCANCEL:
  618.                 PostQuitMessage( 0 );
  619.                 EndDialog( hDlg, IDCANCEL );
  620.                 break; 
  621.  
  622.             case IDC_SOUNDFILE:
  623.                 OnSaveSoundFile( hDlg );
  624.                 break;
  625.  
  626.             case IDC_RECORD:
  627.                 g_bRecording = !g_bRecording;
  628.                 if( FAILED( hr = StartOrStopRecord( g_bRecording ) ) )
  629.                 {
  630.                     DXTRACE_ERR_MSGBOX( TEXT("StartOrStopRecord"), hr );
  631.                     MessageBox( hDlg, L"Error with DirectSoundCapture buffer."                            
  632.                                 L"Sample will now exit.", L"DirectSound Sample", 
  633.                                 MB_OK | MB_ICONERROR );
  634.                     PostQuitMessage( 0 );
  635.                     EndDialog( hDlg, IDABORT );
  636.                 }
  637.  
  638.                 if( !g_bRecording )
  639.                     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE );
  640.  
  641.                 break;
  642.  
  643.             default:
  644.                 return FALSE; // Didn't handle message
  645.         }
  646.         break;
  647.  
  648.     default:
  649.         return FALSE; // Didn't handle message
  650.     }
  651.  
  652.     return TRUE; // Handled message
  653. }
  654.  
  655.  
  656.  
  657.  
  658. //-----------------------------------------------------------------------------
  659. // Name: OnInitMainDialog()
  660. // Desc: Initializes the main dialog
  661. //-----------------------------------------------------------------------------
  662. HRESULT OnInitMainDialog( HWND hDlg )
  663. {
  664.     // Load the icon
  665.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDR_MAINFRAME ) );
  666.  
  667.     // Set the icon for this dialog.
  668.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  669.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  670.  
  671.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE);
  672.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("No file loaded.") );
  673.  
  674.     return S_OK;
  675. }
  676.  
  677.  
  678.  
  679.  
  680. //-----------------------------------------------------------------------------
  681. // Name: DSoundEnumCallback()
  682. // Desc: Enumeration callback called by DirectSoundEnumerate
  683. //-----------------------------------------------------------------------------
  684. INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName,
  685.                                   VOID* pContext )
  686. {
  687.     // Set aside static storage space for 20 audio drivers
  688.     static GUID  AudioDriverGUIDs[20];
  689.     static DWORD dwAudioDriverIndex = 0;
  690.  
  691.     GUID* pTemp  = NULL;
  692.  
  693.     if( pGUID )
  694.     {
  695.         if( dwAudioDriverIndex >= 20 )
  696.             return TRUE;
  697.  
  698.         pTemp = &AudioDriverGUIDs[dwAudioDriverIndex++];
  699.         memcpy( pTemp, pGUID, sizeof(GUID) );
  700.     }
  701.  
  702.     HWND hSoundDeviceCombo = (HWND)pContext;
  703.  
  704.     // Add the string to the combo box
  705.     SendMessage( hSoundDeviceCombo, CB_ADDSTRING, 
  706.                  0, (LPARAM) (LPCTSTR) strDesc );
  707.  
  708.     // Get the index of the string in the combo box
  709.     INT nIndex = (INT)SendMessage( hSoundDeviceCombo, CB_FINDSTRING, 
  710.                                    0, (LPARAM) (LPCTSTR) strDesc );
  711.  
  712.     // Set the item data to a pointer to the static guid stored in AudioDriverGUIDs
  713.     SendMessage( hSoundDeviceCombo, CB_SETITEMDATA, 
  714.                  nIndex, (LPARAM) pTemp );
  715.  
  716.     return TRUE;
  717. }
  718.  
  719.  
  720.  
  721.  
  722. //-----------------------------------------------------------------------------
  723. // Name: OnSaveSoundFile()
  724. // Desc: Called when the user requests to save to a sound file
  725. //-----------------------------------------------------------------------------
  726. VOID OnSaveSoundFile( HWND hDlg ) 
  727. {
  728.     HRESULT hr;
  729.  
  730.     static TCHAR strFileName[MAX_PATH] = TEXT("");
  731.     static TCHAR strPath[MAX_PATH] = TEXT("");
  732.  
  733.     // Setup the OPENFILENAME structure
  734.     OPENFILENAME ofn = { sizeof(OPENFILENAME), hDlg, NULL,
  735.                          TEXT("Wave Files\0*.wav\0All Files\0*.*\0\0"), NULL,
  736.                          0, 1, strFileName, MAX_PATH, NULL, 0, strPath,
  737.                          TEXT("Save Sound File"),
  738.                          OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | 
  739.                          OFN_HIDEREADONLY    | OFN_NOREADONLYRETURN, 
  740.                          0, 0, TEXT(".wav"), 0, NULL, NULL };
  741.  
  742.     // Get the default media path (something like C:\WINDOWS\MEDIA)
  743.     if( '\0' == strPath[0] )
  744.     {
  745.         if( GetWindowsDirectory( strPath, MAX_PATH ) != 0 )
  746.         {
  747.             if( wcscmp( &strPath[wcslen(strPath)], TEXT("\\") ) )
  748.                 wcscat( strPath, TEXT("\\") );
  749.             wcscat( strPath, TEXT("MEDIA") );
  750.         }
  751.     }
  752.  
  753.     if( g_bRecording )
  754.     {
  755.         // Stop the capture and read any data that 
  756.         // was not caught by a notification
  757.         StartOrStopRecord( FALSE );
  758.         g_bRecording = FALSE;
  759.     }
  760.  
  761.     // Update the UI controls to show the sound as loading a file
  762.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), FALSE );
  763.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Saving file...") );
  764.  
  765.     // Display the SaveFileName dialog. Then, try to load the specified file
  766.     if( TRUE != GetSaveFileName( &ofn ) )
  767.     {
  768.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Save aborted.") );
  769.         return;
  770.     }
  771.  
  772.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("") );
  773.  
  774.     SAFE_DELETE( g_pWaveFile );
  775.     g_pWaveFile = new CWaveFile;
  776.     if( NULL == g_pWaveFile )
  777.         return;
  778.  
  779.     // Get the format of the capture buffer in g_wfxCaptureWaveFormat
  780.     WAVEFORMATEX wfxCaptureWaveFormat;
  781.     ZeroMemory( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX) );
  782.     g_pDSBCapture->GetFormat( &wfxCaptureWaveFormat, sizeof(WAVEFORMATEX), NULL );
  783.  
  784.     // Load the wave file
  785.     if( FAILED( hr = g_pWaveFile->Open( strFileName, &wfxCaptureWaveFormat, WAVEFILE_WRITE ) ) )
  786.     {
  787.         DXTRACE_ERR_MSGBOX( TEXT("Open"), hr );
  788.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Can not create wave file.") );
  789.         return;
  790.     }
  791.  
  792.     // Update the UI controls to show the sound as the file is loaded
  793.     SetDlgItemText( hDlg, IDC_FILENAME, strFileName );
  794.     EnableWindow( GetDlgItem( hDlg, IDC_RECORD ), TRUE );
  795.  
  796.     // Remember the path for next time
  797.     wcscpy( strPath, strFileName );
  798.     WCHAR* strLastSlash = wcsrchr( strPath, '\\' );
  799.     if( strLastSlash )
  800.         strLastSlash[0] = '\0';
  801. }
  802.  
  803.  
  804.  
  805.  
  806. //-----------------------------------------------------------------------------
  807. // Name: CreateCaptureBuffer()
  808. // Desc: Creates a capture buffer and sets the format 
  809. //-----------------------------------------------------------------------------
  810. HRESULT CreateCaptureBuffer( WAVEFORMATEX* pwfxInput )
  811. {
  812.     HRESULT hr;
  813.     DSCBUFFERDESC dscbd;
  814.  
  815.     SAFE_RELEASE( g_pDSNotify );
  816.     SAFE_RELEASE( g_pDSBCapture );
  817.  
  818.     // Set the notification size
  819.     g_dwNotifySize = MAX( 1024, pwfxInput->nAvgBytesPerSec / 8 );
  820.     g_dwNotifySize -= g_dwNotifySize % pwfxInput->nBlockAlign;   
  821.  
  822.     // Set the buffer sizes 
  823.     g_dwCaptureBufferSize = g_dwNotifySize * NUM_REC_NOTIFICATIONS;
  824.  
  825.     SAFE_RELEASE( g_pDSNotify );
  826.     SAFE_RELEASE( g_pDSBCapture );
  827.  
  828.     // Create the capture buffer
  829.     ZeroMemory( &dscbd, sizeof(dscbd) );
  830.     dscbd.dwSize        = sizeof(dscbd);
  831.     dscbd.dwBufferBytes = g_dwCaptureBufferSize;
  832.     dscbd.lpwfxFormat   = pwfxInput; // Set the format during creatation
  833.  
  834.     if( FAILED( hr = g_pDSCapture->CreateCaptureBuffer( &dscbd, 
  835.                                                         &g_pDSBCapture, 
  836.                                                         NULL ) ) )
  837.         return DXTRACE_ERR_MSGBOX( TEXT("CreateCaptureBuffer"), hr );
  838.  
  839.     g_dwNextCaptureOffset = 0;
  840.  
  841.     if( FAILED( hr = InitNotifications() ) )
  842.         return DXTRACE_ERR_MSGBOX( TEXT("InitNotifications"), hr );
  843.  
  844.     return S_OK;
  845. }
  846.  
  847.  
  848.  
  849.  
  850. //-----------------------------------------------------------------------------
  851. // Name: InitNotifications()
  852. // Desc: Inits the notifications on the capture buffer which are handled
  853. //       in WinMain()
  854. //-----------------------------------------------------------------------------
  855. HRESULT InitNotifications()
  856. {
  857.     HRESULT hr; 
  858.  
  859.     if( NULL == g_pDSBCapture )
  860.         return E_FAIL;
  861.  
  862.     // Create a notification event, for when the sound stops playing
  863.     if( FAILED( hr = g_pDSBCapture->QueryInterface( IID_IDirectSoundNotify, 
  864.                                                     (VOID**)&g_pDSNotify ) ) )
  865.         return DXTRACE_ERR_MSGBOX( TEXT("QueryInterface"), hr );
  866.  
  867.     // Setup the notification positions
  868.     for( INT i = 0; i < NUM_REC_NOTIFICATIONS; i++ )
  869.     {
  870.         g_aPosNotify[i].dwOffset = (g_dwNotifySize * i) + g_dwNotifySize - 1;
  871.         g_aPosNotify[i].hEventNotify = g_hNotificationEvent;             
  872.     }
  873.     
  874.     // Tell DirectSound when to notify us. the notification will come in the from 
  875.     // of signaled events that are handled in WinMain()
  876.     if( FAILED( hr = g_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, 
  877.                                                             g_aPosNotify ) ) )
  878.         return DXTRACE_ERR_MSGBOX( TEXT("SetNotificationPositions"), hr );
  879.  
  880.     return S_OK;
  881. }
  882.  
  883.  
  884.  
  885.  
  886. //-----------------------------------------------------------------------------
  887. // Name: StartOrStopRecord()
  888. // Desc: Starts or stops the capture buffer from recording
  889. //-----------------------------------------------------------------------------
  890. HRESULT StartOrStopRecord( BOOL bStartRecording )
  891. {
  892.     HRESULT hr;
  893.  
  894.     if( bStartRecording )
  895.     {
  896.         // Create a capture buffer, and tell the capture 
  897.         // buffer to start recording   
  898.         if( FAILED( hr = CreateCaptureBuffer( &g_wfxInput ) ) )
  899.             return DXTRACE_ERR_MSGBOX( TEXT("CreateCaptureBuffer"), hr );
  900.  
  901.         if( FAILED( hr = g_pDSBCapture->Start( DSCBSTART_LOOPING ) ) )
  902.             return DXTRACE_ERR_MSGBOX( TEXT("Start"), hr );
  903.     }
  904.     else
  905.     {
  906.         // Stop the capture and read any data that 
  907.         // was not caught by a notification
  908.         if( NULL == g_pDSBCapture )
  909.             return S_OK;
  910.  
  911.         // Stop the buffer, and read any data that was not 
  912.         // caught by a notification
  913.         if( FAILED( hr = g_pDSBCapture->Stop() ) )
  914.             return DXTRACE_ERR_MSGBOX( TEXT("Stop"), hr );
  915.  
  916.         if( FAILED( hr = RecordCapturedData() ) )
  917.             return DXTRACE_ERR_MSGBOX( TEXT("RecordCapturedData"), hr );
  918.  
  919.         // Close the wav file
  920.         SAFE_DELETE( g_pWaveFile );
  921.     }
  922.  
  923.     return S_OK;
  924. }
  925.  
  926.  
  927.  
  928.  
  929. //-----------------------------------------------------------------------------
  930. // Name: RecordCapturedData()
  931. // Desc: Copies data from the capture buffer to the output buffer 
  932. //-----------------------------------------------------------------------------
  933. HRESULT RecordCapturedData() 
  934. {
  935.     HRESULT hr;
  936.     VOID*   pbCaptureData    = NULL;
  937.     DWORD   dwCaptureLength;
  938.     VOID*   pbCaptureData2   = NULL;
  939.     DWORD   dwCaptureLength2;
  940.     UINT    dwDataWrote;
  941.     DWORD   dwReadPos;
  942.     DWORD   dwCapturePos;
  943.     LONG lLockSize;
  944.  
  945.     if( NULL == g_pDSBCapture )
  946.         return S_FALSE;
  947.     if( NULL == g_pWaveFile )
  948.         return S_FALSE;
  949.  
  950.     if( FAILED( hr = g_pDSBCapture->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) )
  951.         return DXTRACE_ERR_MSGBOX( TEXT("GetCurrentPosition"), hr );
  952.  
  953.     lLockSize = dwReadPos - g_dwNextCaptureOffset;
  954.     if( lLockSize < 0 )
  955.         lLockSize += g_dwCaptureBufferSize;
  956.  
  957.     // Block align lock size so that we are always write on a boundary
  958.     lLockSize -= (lLockSize % g_dwNotifySize);
  959.  
  960.     if( lLockSize == 0 )
  961.         return S_FALSE;
  962.  
  963.     // Lock the capture buffer down
  964.     if( FAILED( hr = g_pDSBCapture->Lock( g_dwNextCaptureOffset, lLockSize, 
  965.                                           &pbCaptureData, &dwCaptureLength, 
  966.                                           &pbCaptureData2, &dwCaptureLength2, 0L ) ) )
  967.         return DXTRACE_ERR_MSGBOX( TEXT("Lock"), hr );
  968.  
  969.     // Write the data into the wav file
  970.     if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength, 
  971.                                               (BYTE*)pbCaptureData, 
  972.                                               &dwDataWrote ) ) )
  973.         return DXTRACE_ERR_MSGBOX( TEXT("Write"), hr );
  974.  
  975.     // Move the capture offset along
  976.     g_dwNextCaptureOffset += dwCaptureLength; 
  977.     g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer
  978.  
  979.     if( pbCaptureData2 != NULL )
  980.     {
  981.         // Write the data into the wav file
  982.         if( FAILED( hr = g_pWaveFile->Write( dwCaptureLength2, 
  983.                                                   (BYTE*)pbCaptureData2, 
  984.                                                   &dwDataWrote ) ) )
  985.             return DXTRACE_ERR_MSGBOX( TEXT("Write"), hr );
  986.  
  987.         // Move the capture offset along
  988.         g_dwNextCaptureOffset += dwCaptureLength2; 
  989.         g_dwNextCaptureOffset %= g_dwCaptureBufferSize; // Circular buffer
  990.     }
  991.  
  992.     // Unlock the capture buffer
  993.     g_pDSBCapture->Unlock( pbCaptureData,  dwCaptureLength, 
  994.                            pbCaptureData2, dwCaptureLength2 );
  995.  
  996.  
  997.     return S_OK;
  998. }
  999.  
  1000.  
  1001.  
  1002.